//! The module for all possible coins.

use crate::{
    fuel_asm::Word,
    fuel_tx::Address,
    fuel_types::{
        AssetId,
        Nonce,
    },
};
use coin::Coin;
use fuel_vm_private::prelude::UtxoId;
use message_coin::MessageCoin;

pub mod coin;
pub mod message_coin;

/// The unique identifier of the coin.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialOrd, PartialEq, Ord, Hash)]
pub enum CoinId {
    /// The UTXO id of the regular coin.
    Utxo(UtxoId),
    /// The unique `nonce` of the `MessageCoin`.
    Message(Nonce),
}

impl From<UtxoId> for CoinId {
    fn from(id: UtxoId) -> Self {
        CoinId::Utxo(id)
    }
}

impl From<Nonce> for CoinId {
    fn from(id: Nonce) -> Self {
        CoinId::Message(id)
    }
}

/// The enum of all kind of coins.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CoinType {
    /// The regular coins generated by the transaction output.
    Coin(Coin),
    /// The bridged coin from the DA layer.
    MessageCoin(MessageCoin),
}

impl CoinType {
    /// Returns the coin unique identifier.
    pub fn coin_id(&self) -> CoinId {
        match self {
            CoinType::Coin(coin) => CoinId::Utxo(coin.utxo_id),
            CoinType::MessageCoin(coin) => CoinId::Message(coin.nonce),
        }
    }

    /// Returns the owner of the coin.
    pub fn owner(&self) -> &Address {
        match self {
            CoinType::Coin(coin) => &coin.owner,
            CoinType::MessageCoin(coin) => &coin.recipient,
        }
    }

    /// Returns the amount of the asset held by the coin.
    pub fn amount(&self) -> Word {
        match self {
            CoinType::Coin(coin) => coin.amount,
            CoinType::MessageCoin(coin) => coin.amount,
        }
    }

    /// Returns the asset held by the coin.
    pub fn asset_id<'a>(&'a self, base_asset_id: &'a AssetId) -> &'a AssetId {
        match self {
            CoinType::Coin(coin) => &coin.asset_id,
            CoinType::MessageCoin(_) => base_asset_id,
        }
    }
}

impl From<Coin> for CoinType {
    fn from(coin: Coin) -> Self {
        CoinType::Coin(coin)
    }
}

impl From<MessageCoin> for CoinType {
    fn from(coin: MessageCoin) -> Self {
        CoinType::MessageCoin(coin)
    }
}
